//Adapted from the Profile Provider Implementation Example on MSDN
//http://msdn2.microsoft.com/en-us/library/ta63b872(VS.80).aspx

using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web.Profile;
using System;

namespace ProfileProviderSample.Provider
{

  public class HDIProfileProvider : System.Web.Profile.ProfileProvider
  {

    #region Class Variables

    private string connectionString;
    private string applicationName;

    #endregion

    #region Properties

    /// <summary>
    /// Name of the application.
    /// </summary>
    /// <returns>String</returns>
    public override string ApplicationName
    {
      get
      {
        return applicationName;
      }

      set
      {
        applicationName = value;
      }
    }
    #endregion

    #region Implemented Abstract Methods from ProfileProvider

    /// <summary>
    /// Initialize the provider.
    /// </summary>
    /// <param name="name">Name of the provider.</param>
    /// <param name="config">Configuration settings.</param>
    /// <remarks></remarks>
    public override void Initialize(
     string name,
     System.Collections.Specialized.NameValueCollection config
    )
    {

      // Initialize values from web.config.
      if (config == null)
      {
        throw new ArgumentNullException("config");
      }

      if ((name == null) || (name.Length == 0))
      {
        name = "HDIProfileProvider";
      }

      if (String.IsNullOrEmpty(config["description"]))
      {
        config.Remove("description");
        config.Add("description", "How Do I Profile provider");
      }

      // Initialize the abstract base class.
      base.Initialize(name, config);

      if ((config["applicationName"] == null) || String.IsNullOrEmpty(config["applicationName"]))
      {
        applicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
      }
      else
      {
        applicationName = config["applicationName"];
      }

      // Initialize connection string.
      ConnectionStringSettings connectionStringSettings =
        ConfigurationManager.ConnectionStrings[config["connectionStringName"]];

      if ((connectionStringSettings == null) || String.IsNullOrEmpty(connectionStringSettings.ConnectionString))
      {

        throw new Exception("Connection String cannot be blank.");
      }
      connectionString = connectionStringSettings.ConnectionString;

    }

    /// <summary>
    /// Get the property values for the user profile.
    /// </summary>
    /// <param name="context">Application context.</param>
    /// <param name="settingsProperties">Profile property settings.</param>
    /// <returns>Property setting values.</returns>
    public override System.Configuration.SettingsPropertyValueCollection GetPropertyValues(System.Configuration.SettingsContext context, System.Configuration.SettingsPropertyCollection settingsProperties)
    {

      string userName = context["UserName"].ToString();
      bool isAuthenticated = Convert.ToBoolean(context["IsAuthenticated"]);

      SqlConnection sqlConnection = new SqlConnection(connectionString);
      SqlCommand sqlCommand = new SqlCommand("Security.GetCustomProfileData", sqlConnection);
      sqlCommand.CommandType = CommandType.StoredProcedure;
      SqlDataReader sqlDataReader = null;

      sqlCommand.Parameters.Add("userName", SqlDbType.NVarChar, 50).Value = userName;
      sqlCommand.Parameters.Add("applicationName", SqlDbType.NVarChar, 50).Value = applicationName;
      sqlCommand.Parameters.Add("isAnonymous", SqlDbType.Bit, 0).Value = !isAuthenticated;

      SettingsPropertyValueCollection settingsValues = new SettingsPropertyValueCollection();

      try
      {
        sqlConnection.Open();
        sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.CloseConnection & CommandBehavior.SingleRow);

        sqlDataReader.Read();

        foreach (SettingsProperty property in settingsProperties)
        {
          SettingsPropertyValue settingsPropertyValue = new SettingsPropertyValue(property);

          if (sqlDataReader.HasRows)
          {
            switch (property.Name)
            {
              case "NumRows":
                settingsPropertyValue.PropertyValue = sqlDataReader["NumRows"];
                break;
              
              default:
                throw new Exception("Unsupported property.");
            }
          }

          settingsValues.Add(settingsPropertyValue);
        }
      }
      catch (SqlException e)
      {
        throw new Exception(e.Message);
      }
      finally
      {
        if (sqlDataReader != null)
        {
          sqlDataReader.Close();
        }
      }

      return settingsValues;
    }

    /// <summary>
    /// Set/store the property values for the user profile.
    /// </summary>
    /// <param name="context">Application context.</param>
    /// <param name="settingsPropertyValues">Profile property value settings.</param>
    public override void SetPropertyValues(System.Configuration.SettingsContext context, System.Configuration.SettingsPropertyValueCollection settingsPropertyValues)
    {

      string userName = context["UserName"].ToString();
      bool isAuthenticated = Convert.ToBoolean(context["IsAuthenticated"]);

      SqlConnection sqlConnection = new SqlConnection(connectionString);
      SqlCommand sqlCommand = new SqlCommand("Security.SetCustomProfileData", sqlConnection);
      sqlCommand.CommandType = CommandType.StoredProcedure;

      sqlCommand.Parameters.Add("userName", SqlDbType.NVarChar, 50).Value = userName;
      sqlCommand.Parameters.Add("applicationName", SqlDbType.NVarChar, 50).Value = applicationName;
      sqlCommand.Parameters.Add("isAnonymous", SqlDbType.Bit, 0).Value = !isAuthenticated;

      foreach (SettingsPropertyValue settingsPropertyValue in settingsPropertyValues)
      {
        switch (settingsPropertyValue.Property.Name)
        {
          case "NumRows":
            sqlCommand.Parameters.Add("NumRows", SqlDbType.Int, 0).Value = Convert.ToInt32(settingsPropertyValue.PropertyValue);
            break;
          default:
            throw new Exception("Unsupported property.");
        }
      }

      try
      {
        sqlConnection.Open();
        sqlCommand.ExecuteNonQuery();
      }
      catch (SqlException e)
      {
        throw new Exception(e.Message);
      }
      finally
      {
        sqlConnection.Close();
      }

    }

    /// <summary>
    /// Deletes profiles that have been inactive since the specified date.
    /// </summary>
    /// <param name="authenticationOption">Current authentication option setting.</param>
    /// <param name="userInactiveSinceDate">Inactivity date for deletion.</param>
    /// <returns>Number of records deleted.</returns>
    public override int DeleteInactiveProfiles(System.Web.Profile.ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
    {

      SqlConnection sqlConnection = new SqlConnection(connectionString);
      SqlCommand sqlCommand = new SqlCommand("Profiles_Inactive_Del", sqlConnection);
      sqlCommand.CommandType = CommandType.StoredProcedure;
      sqlCommand.Connection = sqlConnection;
      SqlTransaction transaction = null;

      sqlCommand.Parameters.Add("deleted", SqlDbType.Int, 0);
      sqlCommand.Parameters["deleted"].Direction = ParameterDirection.Output;
      sqlCommand.Parameters.Add("inactivityDate", SqlDbType.DateTime, 0);
      if (authenticationOption == ProfileAuthenticationOption.Anonymous)
      {
        sqlCommand.Parameters.Add("isAnonymous", SqlDbType.Bit, 0).Value = 1;
      }
      else
      {
        sqlCommand.Parameters.Add("isAnonymous", SqlDbType.Bit, 0).Value = 0;
      }

      try
      {
        sqlConnection.Open();
        sqlCommand.ExecuteNonQuery();
      }
      catch (SqlException e)
      {
        throw new Exception(e.Message);
      }
      finally
      {
        sqlConnection.Close();
      }

      return Convert.ToInt32(sqlCommand.Parameters["deleted"].Value);

    }

    /// <summary>
    /// Delete profiles for an array of user names.
    /// </summary>
    /// <param name="userNames">Array of user names.</param>
    /// <returns>Number of profiles deleted.</returns>
    public override int DeleteProfiles(string[] userNames)
    {

      int deleted = 0;
      SqlConnection sqlConnection = new SqlConnection(connectionString);
      SqlCommand sqlCommand = new SqlCommand("Profiles_Del", sqlConnection);
      sqlCommand.CommandType = CommandType.StoredProcedure;
      sqlCommand.Connection = sqlConnection;
      SqlTransaction transaction = null;


      try
      {
        sqlConnection.Open();
        sqlCommand.Parameters.Add("userName", SqlDbType.NVarChar, 50);

        transaction = sqlConnection.BeginTransaction();

        foreach (string userName in userNames)
        {
          sqlCommand.Parameters.Add("userName", SqlDbType.NVarChar, 50).Value = userName;
          sqlCommand.ExecuteNonQuery();
          deleted += 1;
        }

        transaction.Commit();

      }
      catch (SqlException e)
      {
        try
        {
          transaction.Rollback();
        }
        catch (Exception ex)
        {
        }
        throw new Exception(e.Message);
      }
      finally
      {
        sqlConnection.Close();
      }

      return deleted;
    }

    /// <summary>
    /// Delete profiles based upon the user names in the collection of profiles.
    /// </summary>
    /// <param name="profiles">Collection of profiles.</param>
    /// <returns>Number of profiles deleted.</returns>
    public override int DeleteProfiles(System.Web.Profile.ProfileInfoCollection profiles)
    {
      string[] userNames = new string[profiles.Count];

      int index = 0;
      foreach (ProfileInfo profileInfo in profiles)
      {
        userNames[index] = profileInfo.UserName;
        index += 1;
      }

      return DeleteProfiles(userNames);

    }

    /// <summary>
    /// Get a collection of profiles based upon a user name matching string and inactivity date.
    /// </summary>
    /// <param name="authenticationOption">Current authentication option setting.</param>
    /// <param name="userNameToMatch">Characters representing user name to match (L to R).</param>
    /// <param name="userInactiveSinceDate">Inactivity date for deletion.</param>
    /// <param name="pageIndex"></param>
    /// <param name="pageSize"></param>
    /// <param name="totalRecords">Total records found (output).</param>
    /// <returns>Collection of profiles.</returns>
    public override System.Web.Profile.ProfileInfoCollection FindInactiveProfilesByUserName(System.Web.Profile.ProfileAuthenticationOption authenticationOption, string userNameToMatch, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
    {

      CheckParameters(pageIndex, pageSize);

      return GetProfileInfo(
      authenticationOption,
      userNameToMatch,
      userInactiveSinceDate,
      pageIndex,
      pageSize,
      out totalRecords
      );

    }

    /// <summary>
    /// Get a collection of profiles based upon a user name matching string.
    /// </summary>
    /// <param name="authenticationOption">Current authentication option setting.</param>
    /// <param name="userNameToMatch">Characters representing user name to match (L to R).</param>
    /// <param name="pageIndex"></param>
    /// <param name="pageSize"></param>
    /// <param name="totalRecords">Total records found (output).</param>
    /// <returns>Collection of profiles.</returns>
    public override System.Web.Profile.ProfileInfoCollection FindProfilesByUserName(System.Web.Profile.ProfileAuthenticationOption authenticationOption, string userNameToMatch, int pageIndex, int pageSize, out int totalRecords)
    {

      CheckParameters(pageIndex, pageSize);

      return GetProfileInfo(
      authenticationOption,
      userNameToMatch,
      null,
      pageIndex,
      pageSize,
      out totalRecords
      );

    }

    /// <summary>
    /// Get a collection of profiles based upon an inactivity date.
    /// </summary>
    /// <param name="authenticationOption">Current authentication option setting.</param>
    /// <param name="userInactiveSinceDate">Inactivity date for deletion.</param>
    /// <param name="pageIndex"></param>
    /// <param name="pageSize"></param>
    /// <param name="totalRecords">Total records found (output).</param>
    /// <returns>Collection of profiles.</returns>
    public override System.Web.Profile.ProfileInfoCollection GetAllInactiveProfiles(System.Web.Profile.ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate, int pageIndex, int pageSize, out int totalRecords)
    {



      CheckParameters(pageIndex, pageSize);

      return GetProfileInfo(
      authenticationOption,
      null,
      userInactiveSinceDate,
      pageIndex,
      pageSize,
      out totalRecords
      );

    }

    /// <summary>
    /// Get a collection of profiles.
    /// </summary>
    /// <param name="authenticationOption">Current authentication option setting.</param>
    /// <param name="pageIndex"></param>
    /// <param name="pageSize"></param>
    /// <param name="totalRecords">Total records found (output).</param>
    /// <returns>Collection of profiles.</returns>
    public override System.Web.Profile.ProfileInfoCollection GetAllProfiles(System.Web.Profile.ProfileAuthenticationOption authenticationOption, int pageIndex, int pageSize, out int totalRecords)
    {


      CheckParameters(pageIndex, pageSize);

      return GetProfileInfo(
      authenticationOption,
      null,
      null,
      pageIndex,
      pageSize,
      out totalRecords
      );

    }

    /// <summary>
    /// Get the number of inactive profiles based upon an inactivity date.
    /// </summary>
    /// <param name="authenticationOption">Current authentication option setting.</param>
    /// <param name="userInactiveSinceDate">Inactivity date for deletion.</param>
    /// <returns>Number of profiles.</returns>
    public override int GetNumberOfInactiveProfiles(System.Web.Profile.ProfileAuthenticationOption authenticationOption, DateTime userInactiveSinceDate)
    {

      int inactiveProfiles = 0;

      GetProfileInfo(authenticationOption, null, userInactiveSinceDate, 0, 0, out inactiveProfiles);

      return inactiveProfiles;

    }

    #endregion

    #region Methods

    /// <summary>
    /// Verifies input parameters for page size and page index. 
    /// </summary>
    /// <param name="pageIndex"></param>
    /// <param name="pageSize"></param>
    /// <remarks></remarks>
    private void CheckParameters(int pageIndex, int pageSize)
    {
      if (pageIndex < 0)
      {
        throw new ArgumentException("Page index must 0 or greater.");
      }
      if (pageSize < 1)
      {
        throw new ArgumentException("Page size must be greater than 0.");
      }
    }

    /// <summary>
    /// Get a collection of profiles based upon a user name matching string and inactivity date.
    /// </summary>
    /// <param name="authenticationOption">Current authentication option setting.</param>
    /// <param name="userNameToMatch">Characters representing user name to match (L to R).</param>
    /// <param name="userInactiveSinceDate">Inactivity date for deletion.</param>
    /// <param name="pageIndex"></param>
    /// <param name="pageSize"></param>
    /// <param name="totalRecords">Total records found (output).</param>
    /// <returns>Collection of profiles.</returns>
    private ProfileInfoCollection GetProfileInfo(
     ProfileAuthenticationOption authenticationOption,
     string usernameToMatch,
    object userInactiveSinceDate,
     int pageIndex,
     int pageSize ,
    out int totalRecords
    )
    {

      SqlConnection sqlConnection = new SqlConnection(connectionString);
      SqlCommand sqlCommand = new SqlCommand("Profiles_Sel", sqlConnection);
      sqlCommand.CommandType = CommandType.StoredProcedure;
      sqlCommand.Connection = sqlConnection;
      SqlTransaction transaction = null;

      // if searching for a user name to match add the command text and parameters.
      if (String.IsNullOrEmpty(usernameToMatch))
      {
        sqlCommand.Parameters.Add("userName", SqlDbType.DateTime, 0).Value = usernameToMatch;
      }

      //if searching for inactive profiles add the command text and parameters.
      if (userInactiveSinceDate != null)
      {
        sqlCommand.Parameters.Add("inactivityDate", SqlDbType.DateTime, 0).Value = Convert.ToDateTime(userInactiveSinceDate);
      }

      // If searching for a anonymous or authenticated profiles, add the command text 
      // and parameters.
      if (authenticationOption == ProfileAuthenticationOption.Anonymous)
      {
        sqlCommand.Parameters.Add("isAnonymous", SqlDbType.Bit, 0).Value = 1;
      }
      else
      {
        sqlCommand.Parameters.Add("isAnonymous", SqlDbType.Bit, 0).Value = 0;
      }

      SqlDataReader sqlDataReader = null;
      ProfileInfoCollection profiles = new ProfileInfoCollection();

      // Count profiles only.
      if (pageSize == 0)
      {
        totalRecords = 0;
        return profiles;
      }

      int counter = 0;
      try
      {
        sqlConnection.Open();
        sqlDataReader = sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);

        int startIndex = pageSize * (pageIndex - 1);
        int endIndex = startIndex + pageSize - 1;

        while (sqlDataReader.Read())
        {
          if (counter >= startIndex)
          {
            ProfileInfo profileInfo = new ProfileInfo(
           (sqlDataReader["UserName"].ToString()),
           Convert.ToBoolean(sqlDataReader["IsAnonymous"]),
           Convert.ToDateTime(sqlDataReader["LastActivity"]),
           Convert.ToDateTime(sqlDataReader["LastUpdated"]),
           0
           );

            profiles.Add(profileInfo);
          }

          if (counter >= endIndex)
          {
            sqlCommand.Cancel();
          }

          counter += 1;
        }
      }
      catch (SqlException e)
      {
        throw new Exception(e.Message);
      }
      finally
      {
        sqlConnection.Close();
      }

      totalRecords = counter;

      return profiles;

    }

    #endregion

  }
}
